#include "../include/socket.h"
#include "../include/macro.h"
#include <unistd.h>
#include "../include/conf_reader.h"

#include <pthread.h>
#include "../include/my_log.h"
#include <string.h>
#include <stdlib.h>
#include <cerrno>

connection_s::~connection_s()
{
}


connection_s::connection_s()
{
}
//初始化一个连接


void connection_s::get_one_to_init()
{
    this->fd=-1;
    
    icurrentsequence=(this->icurrentsequence+1)&ICURSEQ_MAX;   //初始化，直接为1，表示连接可用
    
    this->cur_stat=PKG_HEAD_RECING;    //收包头状态
    this->precvbuf=this->package_head;
    this->precvlen=sizeof(struct package_head_s);
    this->recv_message_ptr=nullptr;
    this->wait_to_deal_received_pkgs=0;
    this->wait_to_send_count=0;
    memset(this->package_head,0,20);    //置空一下

    this->send_buf_is_full_flag=0;
    this->sendbuf=nullptr;
    this->sendlen=0;
    this->send_message_ptr=nullptr;

}
//释放连接里的内容
void connection_s::put_one_to_free()
{
    if(this->recv_message_ptr){
        delete recv_message_ptr;
        recv_message_ptr=nullptr;
    }
    if(this->send_message_ptr){
        delete send_message_ptr;
        send_message_ptr=nullptr;
    }
    if(this->fd!=-1){
        close(fd);
        fd=-1;
    }


}
//初始化连接池
bool _socket::init_connection_pool()
{
    connection_p con_p;
    for(int i=0;i<this->work_connections;++i){      //这个配置参数在read_conf里读了
        //创建一个连接
        con_p=new connection_d;
        con_p->icurrentsequence=1;  //刚开始创建就为1个
        //con_p->get_one_to_init();   //初始化各种连接，这里好像没有什么必要，毕竟我取出一个空闲连接时，一定会初始化
        


        this->all_connection.push_back(con_p);  //全部的队列
        this->free_connection.push_back(con_p); //空闲连接队列

    }
    this->free_connections_num=work_connections;
    this->total_connections_num=work_connections;

    my_log * log=my_log::get_log_instance();
    log->write_log(LOG_INFO,0,"成功初始化%d个连接的连接池",work_connections);
    
    return true;
}

//返回一个空闲连接
connection_p _socket::get_one_free_con()
{
    //存在资源竞争
    pthread_mutex_lock(&connection_mtx);    //上锁
    if(this->free_connection.empty()==false){
        connection_p con_p=free_connection.front();
        con_p->get_one_to_init();   //也得初始化一下，加入空闲队列时，由于并不会初始化
        free_connection.pop_front();
        this->free_connections_num--;   //减少一个空闲连接数量
        pthread_mutex_unlock(&connection_mtx);  //解锁
        return con_p;
    }
    //已经没有空闲连接了,申请一个
    connection_p con_p=new(std::nothrow) connection_d();    //使用后，申请失败会返回nullptr

    if(con_p==nullptr){
        //这里写日志
        my_log *log=my_log::get_log_instance();
        log->write_log(LOG_ALERT,0,"new申请失败");
        pthread_mutex_unlock(&connection_mtx);  //解锁
        return nullptr;

    }
    //加入all_connection;
    this->all_connection.push_back(con_p);
    this->total_connections_num++;
    con_p->icurrentsequence=0;  //主要是弄一个初始值，具体是0还是1其实无所谓
    con_p->get_one_to_init();
    
    pthread_mutex_unlock(&connection_mtx);  //解锁

    return con_p;
}

//将监听套接字加入连接池子里
void _socket::add_listening_fd_to_connection_pool()
{
    //将监听套接字的fd放入连接池中
    for(auto it=listening_list.begin();it!=listening_list.end();++it){
        connection_p con_p=this->get_one_free_con();
        if(con_p==nullptr){
            //在这里应该不会出现这个问题，如果出现了，那么程序也无法正常运行了，那么就直接退出去吧，日志也打印了
            exit(1);
        }
        (*it)->connection=con_p;

        con_p->fd=(*it)->fd;
        con_p->listen=(*it);

        //绑定读写事件
        con_p->event_read_handler=&_socket::accept_event_handler;   //绑定处理

        //需要将其加入epoll中去
        epoll_event event;
        event.events=EPOLLIN;
        event.data.ptr=con_p;   //用于到时候触发后，能调用读写事件

        if(epoll_ctl(this->epoll_fd,EPOLL_CTL_ADD,con_p->fd,&event)==-1){
            //写日志，然后退出
            my_log* log=my_log::get_log_instance();
            log->write_log(LOG_ALERT,errno,"监听套接字加入连接池失败,由于epoll_ctl 将监听套接字放入epoll中失败");
            exit(1);
        }
        //加入成功，等待epoll_wait  accept
        my_log * log=my_log::get_log_instance();
        log->write_log(LOG_INFO,0,"成功将监听套接字放入连接池中去");
    }

    my_log::get_log_instance()->write_log(LOG_INFO,0,"成功将一个监听套接字放入连接池中去");
  
}

//直接释放连接----在accept接收连接是调用，由于并没有正式的加入进来，所以this->online_clients并不用处理
bool _socket::add_connection_to_free(connection_p p)
{
    //直接释放该连接即可
    p->put_one_to_free();

    //加锁入空闲队列
    --(this->recycle_connection_num);
    pthread_mutex_lock(&this->connection_mtx);
    this->free_connection.push_back(p);
    this->free_connections_num++;
    
    //这里也不用考虑将icurrsequence置0，这个连接啥事都没干，反正到时候，有需要拿取该连接，直接也会在init中初始化为1
    pthread_mutex_unlock(&this->connection_mtx);
    
    return false;
}

//将连接加入到待释放连接队列中，让处理线程去执行
bool _socket::add_con_to_recycle(connection_p con_p)
{
    if(con_p->fd!=-1){
        close(con_p->fd);
        con_p->fd=-1;       //通过这个也可以判断是否可行
    }
    con_p->icurrentsequence=(con_p->icurrentsequence+1)&ICURSEQ_MAX;  //值不断递增，这样能够检测废包 -----因为只要不相等，那么就意味着已断开或新连接复用了
    //加锁，放入队列中
    //连接是否有可能重复放入？？？------关闭连接可能在epoll_wait监听到事件发生，还有踢人时钟队列会发生，所以可能重放
    //采用map<con_p,int> 
    pthread_mutex_lock(&this->recycle_connection_mtx);
    if(this->is_exists.find(con_p)!=is_exists.end()){
        // 存在，找到了，没有重放的必要
        pthread_mutex_unlock(&this->recycle_connection_mtx);
        return true;

    }
    is_exists[con_p]=1; //表示存在
    this->recycle_connection.push_back(con_p);
    this->online_clients--;
    this->recycle_connection_num++; //连接池子里
    con_p->add_recycle_time=time(NULL); //加入队列的时间，用于延迟回收机制

    pthread_mutex_unlock(&this->recycle_connection_mtx);

    return true;
}


void *_socket::deal_recycle_connection_thread(void *thread_item)
{
    //写这个模块的心路历程

    //这是一个待释放连接池的回收函数---为什么需要延迟回收
    //暂时不是很理解,觉得鸡肋，还存在锁资源的竞争，而且add_con_to_recycle函数只有epoll_wait触发事件后的处理才能调用，而这个调用是单线程的
    

    //由于只有一个接口，能加入该队列，那么且是尾插，那么是不是可以理解成是一个按时间排序的队列？？
    //如果按时间排序，那么是不是可以找到第一个不满足的就不往后拿了----这个可行

    //踢人时钟线程也会加入回收队列，那么就不是按照时间排序，而可能重复加入，所以需要完全遍历
    thread_item_for_socket * item=(thread_item_for_socket *)(thread_item);
    _socket * external_sock=item->external_socket;
    // std::list<connection_p>::iterator it;   
    int wait_time=external_sock->recycle_time;
    std::list<connection_p> recycle_connection_copy;    //用于存放还没到时间的待释放连接
    while(1){
        usleep(1000*1000);   //自己自定义时间-------目前这个时间并不合理，太短了
        if(external_sock->deal_recycle_running_flag==PRO_END){
            //程序要退出，上面已经清空了-----这里也做判断吧
            break;
        }
        pthread_mutex_lock(&external_sock->recycle_connection_mtx);     //上锁
        //将拿取出直到不符合，就不拿取了
        time_t now_time=time(NULL);
        while(!external_sock->recycle_connection.empty()){

            //取出一个，进行时间上的判断
            auto data=external_sock->recycle_connection.front();
            external_sock->recycle_connection.pop_front();  //无论如何都要弹出来
            if(data->add_recycle_time+wait_time<=now_time){
                //可以释放连接了
                my_log::get_log_instance()->write_log(LOG_INFO,0,"成功完全释放一个待回收的连接 ip:%s port:%d",inet_ntoa(data->opposite_addr.sin_addr),ntohs(data->opposite_addr.sin_port));
                
                external_sock->add_connection_to_free(data);
                
                //这里也得把is_exists的标记去除
                external_sock->is_exists.erase(data);
            }else{
                //还未到时间
                recycle_connection_copy.push_back(data);

            }

        }
        pthread_mutex_unlock(&external_sock->recycle_connection_mtx);   //解锁
        if(recycle_connection_copy.empty()==false){
            external_sock->recycle_connection=move(recycle_connection_copy);
        }
        
        // recycle_connection_copy.clear();    //已经move了应该不用这个clear

    }

    //这里其实可以将待回收队列直接回收掉---
    while(!external_sock->recycle_connection.empty()){
        //直接全放入
        auto data=external_sock->recycle_connection.front();
        external_sock->add_connection_to_free(data);
        external_sock->recycle_connection.pop_front();
    }



    
    return NULL;
}

